﻿namespace Microsoft.VisualStudio.BaseCustomTool
{
    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using EnvDTE;
    using Microsoft.VisualStudio.Designer.Interfaces;
    using VSLangProj;

    public abstract class BaseCustomToolWithSite : BaseCustomTool, IObjectWithSite
    {
        private static Guid CodeDomInterfaceGuid = new Guid("{73E59688-C7C4-4a85-AF64-A538754784C5}");
        private CodeDomProvider codeDomProvider;
        private static Guid CodeDomServiceGuid = CodeDomInterfaceGuid;
        private const int E_FAIL = -2147467259;
        private const int E_NOINTERFACE = -2147467262;
        private ServiceProvider serviceProvider;
        private object site;

        protected BaseCustomToolWithSite()
        {
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        protected void AddReferenceDllToProject(string[] referenceDll)
        {
            if (referenceDll.Length != 0)
            {
                object service = this.GetService(typeof(ProjectItem));
                if (service == null)
                {
                    string message = string.Format("Unable to add DLL to project references: {0}.  Please Add them manually.", GetDllNames(referenceDll));
                    this.GeneratorErrorCallback(false, 1, message, 0, 0);
                }
                else
                {
                    Project project = ((ProjectItem)service).ContainingProject;
                    if (project == null)
                    {
                        string str2 = string.Format("Unable to add DLL to project references: {0}.  Please Add them manually.", GetDllNames(referenceDll));
                        this.GeneratorErrorCallback(false, 1, str2, 0, 0);
                    }
                    else
                    {
                        VSProject project2 = project.Object as VSProject;
                        if (project2 == null)
                        {
                            string str3 = string.Format("Unable to add DLL to project references: {0}.  Please Add them manually.", GetDllNames(referenceDll));
                            this.GeneratorErrorCallback(false, 1, str3, 0, 0);
                        }
                        else
                        {
                            try
                            {
                                for (int i = 0; i < referenceDll.Length; i++)
                                {
                                    project2.References.Add(referenceDll[i]);
                                }
                            }
                            catch (Exception)
                            {
                                string str4 = string.Format("Unable to add DLL to project references: {0}.  Please Add them manually.", GetDllNames(referenceDll));
                                this.GeneratorErrorCallback(false, 1, str4, 0, 0);
                            }
                        }
                    }
                }
            }
        }

        protected virtual string CreateExceptionMessage(Exception exception)
        {
            string str = exception.Message ?? string.Empty;

            for (Exception ex = exception.InnerException; ex != null; ex = ex.InnerException)
            {
                string message = ex.Message;
                if (!string.IsNullOrEmpty(message))
                {
                    str = str + " " + message;
                }
            }

            return str;
        }

        protected virtual void GenerateVersionComment(System.CodeDom.CodeNamespace codeNamespace)
        {
            codeNamespace.Comments.Add(new CodeCommentStatement(string.Empty));
            codeNamespace.Comments.Add(new CodeCommentStatement(string.Format("This source code was auto-generated by {0}, Version {1}.", Assembly.GetExecutingAssembly().GetName().Name, Environment.Version.ToString())));
            codeNamespace.Comments.Add(new CodeCommentStatement(string.Empty));
        }

        public override string GetDefaultExtension()
        {
            string fileExtension = this.CodeDomProvider.FileExtension;
            if (!string.IsNullOrEmpty(fileExtension) && fileExtension[0] != '.')
            {
                fileExtension = "." + fileExtension;
            }

            return fileExtension;
        }

        private static string GetDllNames(string[] dllToAdd)
        {
            if ((dllToAdd == null) || (dllToAdd.Length == 0))
            {
                return string.Empty;
            }
            string str = dllToAdd[0];
            for (int i = 1; i < dllToAdd.Length; i++)
            {
                str = str + ", " + dllToAdd[i];
            }
            return str;
        }

        protected object GetService(Guid service)
        {
            return this.SiteServiceProvider.GetService(service);
        }

        protected object GetService(Type service)
        {
            return this.SiteServiceProvider.GetService(service);
        }

        #region IObjectWithSite Members

        public virtual void GetSite(ref Guid riid, object[] ppvSite)
        {
            if (ppvSite == null)
            {
                throw new ArgumentNullException("ppvSite");
            }

            if (ppvSite.Length < 1)
            {
                throw new ArgumentException("ppvSite array must have at least 1 member", "ppvSite");
            }

            if (this.site == null)
            {
                throw new COMException("object is not sited", -2147467259);
            }

            IntPtr iUnknownForObject = Marshal.GetIUnknownForObject(this.site);
            IntPtr zero = IntPtr.Zero;
            Marshal.QueryInterface(iUnknownForObject, ref riid, out zero);
            if (zero == IntPtr.Zero)
            {
                throw new COMException("site does not support requested interface", -2147467262);
            }

            ppvSite[0] = Marshal.GetObjectForIUnknown(zero);
        }

        public virtual void SetSite(object pUnkSite)
        {
            this.site = pUnkSite;
            this.codeDomProvider = null;
            this.serviceProvider = null;
        }

        #endregion

        protected virtual CodeDomProvider CodeDomProvider
        {
            get
            {
                if (this.codeDomProvider == null)
                {
                    IVSMDCodeDomProvider service = (IVSMDCodeDomProvider)this.GetService(CodeDomServiceGuid);
                    if (service != null)
                    {
                        this.codeDomProvider = (CodeDomProvider)service.CodeDomProvider;
                    }
                }
                return this.codeDomProvider;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }
                this.codeDomProvider = value;
            }
        }

        private ServiceProvider SiteServiceProvider
        {
            get
            {
                if (this.serviceProvider == null)
                {
                    this.serviceProvider = new ServiceProvider(this.site as IOleServiceProvider);
                }
                return this.serviceProvider;
            }
        }
    }
}

